use futures::StreamExt;
use rustls::pki_types::{CertificateDer, PrivateKeyDer};
use rustls_pemfile::{certs, pkcs8_private_keys};
use std::{
    fs::File,
    future,
    io::{self, BufReader},
    net::{IpAddr, Ipv4Addr},
    path::Path,
    sync::Arc,
};
use tarpc::{
    context,
    server::{self, incoming::Incoming, Channel},
    tokio_serde::formats::Bincode,
};
use tokio_rustls::TlsAcceptor;

#[tarpc::service]
trait HelloService {
    async fn hello(string: String);
}

#[derive(Clone)]
struct HelloServiceServer;

#[tarpc::server]
impl HelloService for HelloServiceServer {
    async fn hello(self, _: context::Context, string: String) {
        println!("Hello from client: {}", string)
    }
}

fn load_certs(path: &Path) -> io::Result<Vec<CertificateDer<'static>>> {
    certs(&mut BufReader::new(File::open(path)?)).collect()
}

fn load_keys(path: &Path) -> io::Result<PrivateKeyDer<'static>> {
    pkcs8_private_keys(&mut BufReader::new(File::open(path)?))
        .next()
        .unwrap()
        .map(Into::into)
}

#[tokio::main]
async fn main() {
    let server_addr = (IpAddr::V4(Ipv4Addr::LOCALHOST), 1234);

    let certs = load_certs(Path::new("cert.pem")).unwrap();
    let key = load_keys(Path::new("cert.key")).unwrap();
    let config = rustls::ServerConfig::builder()
        .with_no_client_auth()
        .with_single_cert(certs, key)
        .map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))
        .unwrap();
    let acceptor = TlsAcceptor::from(Arc::new(config));

    let mut listener = tarpc_rustls::tls::listen(&server_addr, acceptor, Bincode::default)
        .await
        .unwrap();
    listener.config_mut().max_frame_length(1024usize);
    listener
        .filter_map(|r| {
            future::ready(match r {
                Ok(r) => Some(r),
                Err(err) => {
                    eprintln!("Incoming connection failed: {:?}", err);
                    None
                }
            })
        })
        .map(server::BaseChannel::with_defaults)
        .max_channels_per_key(1, |t| t.transport().peer_addr().unwrap().ip())
        .map(|channel| {
            let server = HelloServiceServer;
            channel.execute(server.serve())
        })
        .buffer_unordered(256)
        .for_each(|_| async {})
        .await;
}
